package server

import (
	"context"
	"fmt"
	"log"
	"sync"
	"time"

	apiv1 "quipus/internal/server/router/api/v1"
	"quipus/internal/server/router/frontend"
	"quipus/internal/server/router/manager"

	"github.com/go-faster/errors"
	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/websocket/v2"
	grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

func (s *FiberServer) RegisterFiberRoutes(ctx context.Context) error {

	s.App.Get("/helloworld", s.HelloWorldHandler)

	s.App.Get("/health", s.healthHandler)

	s.App.Use("/ws", func(c *fiber.Ctx) error {
		if websocket.IsWebSocketUpgrade(c) { // Returns true if the client requested upgrade to the WebSocket protocol
			return c.Next()
		}
		return c.SendStatus(fiber.StatusUpgradeRequired)
	})

	wsManager := &manager.WebSocketManager{
		Connections: make(map[string]func(*websocket.Conn)), // map of connection IDs to connection functions
		InitMessage: make(map[string]string),
		MutexMap:    make(map[string]*sync.Mutex),
	}

	s.App.Get("/ws/:knotUID", websocket.New(func(c *websocket.Conn) {
		knotUID := c.Params("knotUID")
		log.Printf("WebSocket connection established for knotUID: %s", knotUID)
		wsManager.Handle(knotUID, c)
	}))

	// frontend.NewFrontendService(s.profile).Serve(ctx, s.App)

	var grpcServer *grpc.Server
	if s.profile.Mode == "demo" {
		log.Println("Running in demo mode")
		grpcServer = grpc.NewServer(
			// Override the maximum receiving message size to 100M for uploading large resources.
			grpc.MaxRecvMsgSize(100*1024*1024),
			grpc.ChainUnaryInterceptor(
				apiv1.NewLoggerInterceptor().LoggerInterceptor,
				grpc_recovery.UnaryServerInterceptor(),
				apiv1.NewGRPCAuthInterceptor(&s.db, s.secret, s.gdsAccess.Handler).AuthenticationInterceptor,
			))
	} else {
		creds, err := credentials.NewServerTLSFromFile(s.profile.CertFile, s.profile.KeyFile)
		if err != nil {
			return err
		}

		grpcServer = grpc.NewServer(grpc.Creds(creds),
			// Override the maximum receiving message size to 100M for uploading large resources.
			grpc.MaxRecvMsgSize(100*1024*1024),
			grpc.ChainUnaryInterceptor(
				apiv1.NewLoggerInterceptor().LoggerInterceptor,
				grpc_recovery.UnaryServerInterceptor(),
				apiv1.NewGRPCAuthInterceptor(&s.db, s.secret, s.gdsAccess.Handler).AuthenticationInterceptor,
			))
	}
	s.grpcServer = grpcServer

	apiV1Service := apiv1.NewAPIV1Service(s.secret, s.profile, grpcServer, s.gdsAccess)
	apiV1Service.WsManager = wsManager

	// Register gRPC gateway as api v1.
	if err := apiV1Service.RegisterGateway(ctx, s.App); err != nil {
		return errors.Wrap(err, "failed to register gRPC gateway")
	}

	frontend.NewFrontendService(s.profile).Serve(ctx, s.App)
	return nil
}

func (s *FiberServer) HelloWorldHandler(c *fiber.Ctx) error {
	resp := fiber.Map{
		"message": "Hello World",
	}
	return c.JSON(resp)
}

func (s *FiberServer) healthHandler(c *fiber.Ctx) error {
	return c.JSON(s.db.Health())
}

func (s *FiberServer) websocketHandler(con *websocket.Conn) {
	ctx, cancel := context.WithCancel(context.Background())

	go func() {
		for {
			_, _, err := con.ReadMessage()
			if err != nil {
				cancel()
				log.Println("Receiver Closing", err)
				break
			}
		}
	}()

	for {
		select {
		case <-ctx.Done():
			return
		default:
			payload := fmt.Sprintf("server timestamp: %d", time.Now().UnixNano())
			if err := con.WriteMessage(websocket.TextMessage, []byte(payload)); err != nil {
				log.Printf("could not write to socket: %v", err)
				return
			}
			time.Sleep(time.Second * 2)
		}
	}
}
